#pragma once

#include <string>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Thread.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"

using namespace std;

const int default_threadnum = 3;

namespace ThreadNS  // NameSpace
{
    class ThreadDate
    {
    public:
        ThreadDate(const string &name)
            : threadname(name)
        {
        }
        ~ThreadDate()
        {
        }

    public:
        string threadname;
    };

    template <class T>
    class ThreadPool
    {
    public:
        static ThreadPool *GetInstance()
        {
            if (_psl == nullptr)
            {
                LockGuard lock(&_sig_lock);
                if (_psl == nullptr)
                {
                    lg.LogMessage(Info, "create singleton is success\n");
                    _psl = new ThreadPool<T>();
                }
            }
            return _psl;
        }

        static void DelInstance()
        {
            if (_psl)
            {
                LockGuard lock(&_sig_lock);
                if (_psl)
                {
                    delete _psl;
                    _psl = nullptr;
                    
                    lg.LogMessage(Info, "destroy singleton is success\n");
                }
            }
        }

        // 启动所有线程
        bool Start()
        {
            for (auto &t : _threads)
            {
                t.Start();
                lg.LogMessage(Info, "%s , is running...\n", t.ThreadName().c_str());
            }

            return true;
        }

        // 等待所有线程终止
        void Join()
        {
            for (auto &t : _threads)
            {
                t.Join();
            }
        }

        // 线程等待当前条件变量
        void Thread_Wait(const ThreadDate &td)
        {
            pthread_cond_wait(&_cond, &_mutex);
            lg.LogMessage(Debug, "no task , %s is sleeping...\n", td.threadname.c_str());
        }

        // 唤醒当前条件变量下的某个线程
        void Thread_Wakeup()
        {
            pthread_cond_signal(&_cond);
        }

        // 添加任务
        bool Push(T &in)
        {
            LockGuard lockguard(&_mutex);
            _q.push(in);
            Thread_Wakeup();

            return true;
        }

        // 线程需要执行的回调函数
        void ThreadRun(const ThreadDate &td)
        {
            while (1)
            {
                T t;

                // 取任务
                {
                    LockGuard lockguard(&_mutex);

                    while (_q.empty())
                    {
                        Thread_Wait(td);
                        lg.LogMessage(Debug, "haven task , %s is wakeup\n", td.threadname.c_str());
                    }
                    t = _q.front();
                    _q.pop();
                }
                // 执行任务
                t();
                // lg.LogMessage(Debug, "%s handler task %s done , result is %s\n",
                //               td.threadname.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str());
            }
        }

    private:
        ThreadPool(int num = default_threadnum)
            : _threadnum(num)
        {
            // 初始化锁和条件变量
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_cond, nullptr);

            // 创建线程
            for (int i = 0; i < _threadnum; i++)
            {
                string threadname = "thread-" + to_string(i + 1);
                ThreadDate td(threadname);
                // 由于Thread执行的是线程池的类内函数，而Thread调用的函数中并没有this指针
                // 所以这里就使用bind函数，调整一下Thread调用函数的参数，使函数可以多接收一个参数
                _threads.push_back(Thread<ThreadDate>(threadname, bind(&ThreadPool<T>::ThreadRun, this, placeholders::_1), td));
                lg.LogMessage(Info, "%s is create\n", threadname.c_str());
            }
        }

        // 销毁锁和条件变量
        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }

        ThreadPool(const ThreadPool&) = delete;
        const ThreadPool& operator=(const ThreadPool&) = delete;

    private:
        queue<T> _q;
        vector<Thread<ThreadDate>> _threads;
        int _threadnum;
        static ThreadPool<T> *_psl;
        static pthread_mutex_t _sig_lock;

        pthread_mutex_t _mutex;
        pthread_cond_t _cond;
    };

    template <class T>
    ThreadPool<T> *ThreadPool<T>::_psl = nullptr;
    template <class T>
    pthread_mutex_t ThreadPool<T>::_sig_lock = PTHREAD_MUTEX_INITIALIZER;
}